package com.googlecode.nls4j;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;

/**
 * This implementation of {@linkplain BundleLoader} is based on
 * {@linkplain ResourceBundle}. The resources are loaded using the
 * {@linkplain ResourceBundle#getBundle(String, Locale, ClassLoader, Control)}
 * method and wrapped into {@linkplain ResourceBundleWrapper}.
 * 
 * @author Andrey Pavlenko
 */
public class ResourceBundleLoader<K extends NlsKey<V>, V> implements
        BundleLoader<K, V> {
    private final String baseName;
    private final ClassLoader loader;
    private final ResourceBundle.Control control;

    /**
     * Same as
     * {@link #ResourceBundleLoader(String, ClassLoader, ResourceBundle.Control)
     * ResourceBundleLoader(null, null, null)}.
     */
    public ResourceBundleLoader() {
        this(null, null, null);
    }

    /**
     * Same as
     * {@link #ResourceBundleLoader(String, ClassLoader, ResourceBundle.Control)
     * ResourceBundleLoader(baseName, null, null)}.
     */
    public ResourceBundleLoader(final String baseName) {
        this(baseName, null, null);
    }

    /**
     * Same as
     * {@link #ResourceBundleLoader(String, ClassLoader, ResourceBundle.Control)
     * ResourceBundleLoader(baseName, loader, null)}.
     */
    public ResourceBundleLoader(final String baseName, final ClassLoader loader) {
        this(baseName, loader, null);
    }

    //@formatter:off
    /**
     * Create new ResourceBundleLoader with the specified parameters.
     * 
     * @param baseName
     *            class name of resource bundle, if null 
     *            keyType.{@linkplain Class#getName() getName()} will be used.
     * @param loader
     *            the class loader to use for loading resources, if null
     *            keyType.{@linkplain Class#getClassLoader() getClassLoader()}
     *            will be used.
     * @param control
     *            the control which gives information for the resource bundle
     *            loading process, if null the method
     *            {@linkplain ResourceBundle#getBundle(String, Locale, ClassLoader)}
     *            will be used for resource loading.
     */
    //@formatter:on
    public ResourceBundleLoader(final String baseName,
            final ClassLoader loader, final Control control) {
        this.baseName = baseName;
        this.loader = loader;
        this.control = control;
    }

    /**
     * {@inheritDoc} <br/>
     * This method loads resources using the
     * {@linkplain #loadResourceBundle(String, Locale, ClassLoader, ResourceBundle.Control)}
     * method and than converts the loaded {@linkplain ResourceBundle} to
     * {@linkplain NlsBundle} using the
     * {@linkplain #toNlsBundle(Class, ResourceBundle)} method.
     */
    @Override
    public NlsBundle<K, V> loadBundle(final Class<K> keyType,
            final Locale locale) throws IllegalArgumentException,
            NullPointerException {
        String baseName = getBaseName();
        ClassLoader loader = getLoader();

        if (baseName == null) {
            baseName = keyType.getName();
        }
        if (loader == null) {
            loader = keyType.getClassLoader();
        }
        if (loader == null) {
            loader = Thread.currentThread().getContextClassLoader();
        }

        try {
            return toNlsBundle(keyType,
                loadResourceBundle(baseName, locale, loader, getControl()));
        } catch (final MissingResourceException ex) {
            return null;
        }
    }

    /**
     * {@inheritDoc} <br/>
     * This implementation returns
     * {@linkplain DefaultValuesBundle#getInstance(Locale)
     * DefaultValuesBundle.<K, V> getInstance(locale)}.
     */
    @Override
    public NlsBundle<K, V> getDefaultBundle(final Class<K> keyType,
            final Locale locale) throws IllegalArgumentException,
            NullPointerException {
        return DefaultValuesBundle.<K, V> getInstance(locale);
    }

    /**
     * Returns the class name of resource bundle specified in the constructor.
     */
    public String getBaseName() {
        return baseName;
    }

    /**
     * Returns the class loader specified in the constructor.
     */
    public ClassLoader getLoader() {
        return loader;
    }

    /**
     * Returns the control specified in the constructor.
     */
    public ResourceBundle.Control getControl() {
        return control;
    }

    /**
     * Loads a ResourceBundle using either
     * {@linkplain ResourceBundle#getBundle(String, Locale, ClassLoader)} (if
     * {@code control} == null) or
     * {@linkplain ResourceBundle#getBundle(String, Locale, ClassLoader, Control)}
     * (if {@code control} != null). The arguments {@code baseName},
     * {@code locale} and {@code loader} must not be null.
     */
    protected ResourceBundle loadResourceBundle(final String baseName,
            final Locale locale, final ClassLoader loader,
            final ResourceBundle.Control control) {
        return (control == null) ? ResourceBundle.getBundle(baseName, locale,
            loader) : ResourceBundle.getBundle(baseName, locale, loader,
            control);
    }

    /**
     * Converts the specified {@linkplain ResourceBundle} to
     * {@linkplain NlsBundle}. The default implementation returns new
     * {@code ResourceBundleWrapper<K, V>(resourceBundle)}.
     */
    protected NlsBundle<K, V> toNlsBundle(final Class<K> keyType,
            final ResourceBundle resourceBundle) {
        return new ResourceBundleWrapper<K, V>(resourceBundle);
    }
}
